package logic.ships.moveableShip;

import java.util.ArrayList;
import java.util.Collection;

import networking.Synchronizable;
import networking.messages.createMessages.ShipCreateMessage;
import networking.messages.createMessages.ShipType;
import networking.messages.syncMessages.NodeSyncMessage;
import networking.messages.syncMessages.ValueType;
import networking.messages.weaponMessages.WeaponFireRemoveMessage;

import input.StraightForwardBinding;
import input.pilot.StrafeController;

import com.captiveimagination.jgn.synchronization.message.SynchronizeCreateMessage;
import com.jme.input.controls.GameControl;
import com.jme.math.Vector3f;
import com.jme.scene.Node;

import logic.common.team.Team;
import logic.nodes.MoveForwardController;
import logic.nodes.collision.AntiGravController;
import logic.nodes.collision.DummyType;
import logic.nodes.nodeSettings.upgrades.UpgradeEvent;
import logic.nodes.nodeSettings.upgrades.UpgradeListener;
import logic.ships.Ship;
import logic.ships.hunter.Hunter;
import logic.trails.ShipTrail;

public abstract class MovableShip extends Ship implements Synchronizable {
	
	private static final long serialVersionUID = 1L;
	
	protected boolean trailEnabled;
	
	protected MoveForwardController moveForwardController;
	protected StrafeController strafeController;
	protected Vector3f currentTargetPosition;
	protected AntiGravController antiGravController;

	protected Collection<ShipTrail> trails;
	protected Node trailsNode;
	
	protected float defaultEngineSpeed;
	
	public MovableShip(String modelPath, String title, Node model, MovableShipProperties properties, Team team) {
		this(modelPath, title, model, properties, team, true, DummyType.None);
	}
	
	public MovableShip(String modelPath, String title, Node model, MovableShipProperties properties, Team team, 
			DummyType dummyType) {
		this(modelPath, title, model, properties, team, true, dummyType);
	}
	
	public MovableShip(String modelPath, String title, Node model, MovableShipProperties properties, Team team, 
			boolean initNozzles, DummyType dummyType) {
		super(modelPath, title, model, properties, team, dummyType);
		
		if(initNozzles) {
			trails = new ArrayList<ShipTrail>();
			trailsNode = new Node("Ship Trails Node");
			attachChild(trailsNode);
			new NozzleSlotCreator(this);
		}
	}
	
	@Override
	protected void applyDummyType() {
		super.applyDummyType();
		
		if(dummyType == DummyType.None && canMove()) {
			antiGravController = new AntiGravController(this);
			ingameState.getThreadPool().registerController(antiGravController);
		}
	}
	
	@Override
	public boolean canMove() { return true; }
	
	public void applyWeaponFireRemoveMessage(WeaponFireRemoveMessage wfrm) {
		Vector3f dir = wfrm.getDir();
		float speed = wfrm.getSpeed();
		createImpulse(speed, dir);
	}
	
	public void removeAntiGravController() {
		if(antiGravController != null) 
			ingameState.getThreadPool().unregisterController(antiGravController);
	}
	
	public void changeSpeed(float speed) {
		valueManager.changeValue(ValueType.Speed, speed);
	}
	
	public void addTrail(ShipTrail trail) {
		trails.add(trail);
		trailsNode.attachChild(trail);
	}
	
	@Override
	public void destroy() {
		super.destroy();
	}
	
	public void createMoveForwardController() {
		float maxSpeed = getMaxForwardSpeed();
		float acc = getForwardAcceleration();
		float beaking = getStrafeBreaking();
		float boostSpeed = getMaxBoostSpeed();
		GameControl forward = StraightForwardBinding.getGameControl(StraightForwardBinding.forward);
		GameControl backward = StraightForwardBinding.getGameControl(StraightForwardBinding.backward);
		moveForwardController = new MoveForwardController(this, forward, maxSpeed, boostSpeed, acc, backward, beaking);
		moveForwardController.stop(true);
		setMoveForwardController(moveForwardController);
	}
	
	protected void updateMoveControllers() {
		if(moveForwardController != null) {
			moveForwardController.setBoostMaxSpeed(getMaxBoostSpeed());
			moveForwardController.setBreaking(getForwardBreaking());
			moveForwardController.setNormalAcceleration(getForwardAcceleration());
			moveForwardController.setNormalMaxSpeed(getMaxForwardSpeed());
		}
		if(strafeController != null) {
			strafeController.setBreaking(getStrafeBreaking());
			strafeController.setNormalAcceleration(getStrafeAcceleration());
			strafeController.setNormalMaxSpeed(getMaxStrafeSpeed());
		}
	}
	
	public void setMoveForwardController(MoveForwardController controller) {
		if(moveForwardController != null) {
			removeController(moveForwardController);
			removeMovingController(moveForwardController);
		}
		if(this instanceof Hunter) ((Hunter)this).addHunterFlightController(controller);
		else addController(controller);
		
		addMovingController(controller);
		moveForwardController = controller;
	}
	
	@Override
	protected void addUpgradeListeners() {
		super.addUpgradeListeners();
		settings.addUpgradeListener(new UpgradeListener() {
			@Override
			public void upgraded(UpgradeEvent event) {
				updateMoveControllers();
			}
		});
	}
	
	public MoveForwardController getMoveForwardController() { return moveForwardController; }
	
	public void setStrafeController(StrafeController controller) {
		if(strafeController != null) {
			removeController(strafeController);
			removeMovingController(strafeController);
		}
		if(this instanceof Hunter) ((Hunter)this).addHunterFlightController(controller);
		else addController(controller);
		
		addMovingController(controller);
		strafeController = controller;
	}
	
	public StrafeController getStrafeController() { return strafeController; }
	
	public float getFrontSpeed() { return getSpeedInDirection(getFrontDirection()); }
	
	public float getStrafeSpeed() {
		if(strafeController == null) return 0f;
		return strafeController.getCurrentSpeed();
	}
	
	public float getCurrentEngineSpeed() {
		if(moveForwardController == null) return defaultEngineSpeed;
		return moveForwardController.getCurrentSpeed();
	}
	
	@Override
	public SynchronizeCreateMessage getCreateMessage() {
		return new ShipCreateMessage(team.getFraction(), getShipType(), objectID);
	}
	
	protected abstract ShipType getShipType();
	
	public MovableShipProperties getMovableShipProperties() { return (MovableShipProperties)settings; }
	
	public float getMaxForwardSpeed() { return getMovableShipProperties().getMaxForwardSpeed(); }
	
	public float getMaxBoostSpeed() { return getMovableShipProperties().getMaxBoostSpeed(); }
	
	public float getMaxStrafeSpeed() { return getMovableShipProperties().getMaxStrafeSpeed(); }
	
	public float getTurnSpeed() { return getMovableShipProperties().getTurnSpeed(); }
	
	public float getForwardAcceleration() { return getMovableShipProperties().getForwardAcceleration(); }
	
	public float getForwardBreaking() { return getMovableShipProperties().getForwardBreaking(); }
	
	public float getStrafeAcceleration() { return getMovableShipProperties().getStrafeAcceleration(); }
	
	public float getStrafeBreaking() { return getMovableShipProperties().getStrafeBreaking(); }
	
	public String getEngineSound() { return getMovableShipProperties().getEngineSound(); }
	
	public void updateTrailQuality() {
		if(trails != null) {
			for(ShipTrail trail : trails) {
				trail.updateGraphicSettings();
			}
		}
	}
	
	@Override
	public boolean applySyncMessage(NodeSyncMessage shipSyncMessage) {
		if(shipSyncMessage.getType() == ValueType.Speed) {
			defaultEngineSpeed = shipSyncMessage.getFloatValue();
			return true;
		}
		return super.applySyncMessage(shipSyncMessage);
	}
}